home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Extensions / Imaging / PIL / PcfFontFile.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  5.8 KB  |  247 lines

  1. #
  2. # THIS IS WORK IN PROGRESS
  3. #
  4. # The Python Imaging Library
  5. # $Id: PcfFontFile.py,v 1.1.1.2 1999/01/13 09:40:23 sjoerd Exp $
  6. #
  7. # portable compiled font file parser
  8. #
  9. # history:
  10. # 97-08-19 fl   created
  11. #
  12. # Copyright (c) Secret Labs AB 1997-98.
  13. # Copyright (c) Fredrik Lundh 1997.
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17.  
  18. import Image
  19. import FontFile
  20.  
  21. import string
  22.  
  23. # --------------------------------------------------------------------
  24. # declarations
  25.  
  26. PCF_MAGIC = 0x70636601
  27.  
  28. PCF_PROPERTIES = (1<<0)
  29. PCF_ACCELERATORS = (1<<1)
  30. PCF_METRICS = (1<<2)
  31. PCF_BITMAPS = (1<<3)
  32. PCF_INK_METRICS    = (1<<4)
  33. PCF_BDF_ENCODINGS = (1<<5)
  34. PCF_SWIDTHS = (1<<6)
  35. PCF_GLYPH_NAMES    = (1<<7)
  36. PCF_BDF_ACCELERATORS = (1<<8)
  37.  
  38. BYTES_PER_ROW = [
  39.     lambda bits: ((bits+7)  >> 3),
  40.     lambda bits: ((bits+15) >> 3) & ~1,
  41.     lambda bits: ((bits+31) >> 3) & ~3,
  42.     lambda bits: ((bits+63) >> 3) & ~7,
  43. ]
  44.  
  45.  
  46. def l16(c):
  47.     return ord(c[0]) + (ord(c[1])<<8)
  48. def l32(c):
  49.     return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
  50.  
  51. def b16(c):
  52.     return ord(c[1]) + (ord(c[0])<<8)
  53. def b32(c):
  54.     return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
  55.  
  56. def sz(s, o):
  57.     return s[o:string.index(s, "\0", o)]
  58.  
  59.  
  60. # --------------------------------------------------------------------
  61. # parser
  62.  
  63. class PcfFontFile(FontFile.FontFile):
  64.  
  65.     name = "name"
  66.  
  67.     def __init__(self, fp):
  68.  
  69.         magic = l32(fp.read(4))
  70.         if magic != PCF_MAGIC:
  71.             raise SyntaxError, "not a PCF file"
  72.  
  73.         FontFile.FontFile.__init__(self)
  74.  
  75.         count = l32(fp.read(4))
  76.         self.toc = {}
  77.         for i in range(count):
  78.             type = l32(fp.read(4))
  79.             self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4))
  80.  
  81.         self.fp = fp
  82.  
  83.         self.info = self._load_properties()
  84.  
  85.         metrics = self._load_metrics()
  86.         bitmaps = self._load_bitmaps(metrics)
  87.         encoding = self._load_encoding()
  88.  
  89.         #
  90.         # create glyph structure
  91.  
  92.         for ch in range(256):
  93.             ix = encoding[ch]
  94.             if ix is not None:
  95.                 x, y, l, r, w, a, d, f = metrics[ix]
  96.                 self.glyph[ch] = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix]
  97.  
  98.  
  99.     def _getformat(self, tag):
  100.  
  101.         format, size, offset = self.toc[tag]
  102.  
  103.         fp = self.fp
  104.         fp.seek(offset)
  105.  
  106.         format = l32(fp.read(4))
  107.  
  108.         if format & 4:
  109.             i16, i32 = b16, b32
  110.         else:
  111.             i16, i32 = l16, l32
  112.  
  113.         return fp, format, i16, i32
  114.  
  115.     def _load_properties(self):
  116.  
  117.         #
  118.         # font properties
  119.  
  120.         properties = {}
  121.  
  122.         fp, format, i16, i32 = self._getformat(PCF_PROPERTIES)
  123.  
  124.         nprops = i32(fp.read(4))
  125.  
  126.         # read property description
  127.         p = []
  128.         for i in range(nprops):
  129.             p.append(i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4)))
  130.         if nprops & 3:
  131.             fp.seek(4 - (nprops & 3), 1) # pad
  132.  
  133.         data = fp.read(i32(fp.read(4)))
  134.  
  135.         for k, s, v in p:
  136.             k = sz(data, k)
  137.             if s:
  138.                 v = sz(data, v)
  139.             properties[k] = v
  140.  
  141.         return properties
  142.  
  143.     def _load_metrics(self):
  144.  
  145.         #
  146.         # font metrics
  147.  
  148.         metrics = []
  149.  
  150.         fp, format, i16, i32 = self._getformat(PCF_METRICS)
  151.  
  152.         append = metrics.append
  153.  
  154.         if (format & 0xff00) == 0x100:
  155.  
  156.             # "compressed" metrics
  157.             for i in range(i16(fp.read(2))):
  158.                 left = ord(fp.read(1)) - 128
  159.                 right = ord(fp.read(1)) - 128
  160.                 width = ord(fp.read(1)) - 128
  161.                 ascent = ord(fp.read(1)) - 128
  162.                 descent = ord(fp.read(1)) - 128
  163.                 xsize = right - left
  164.                 ysize = ascent + descent
  165.                 append((xsize, ysize, left, right, width, ascent, descent, 0))
  166.  
  167.         else:
  168.  
  169.             # "jumbo" metrics
  170.             for i in range(i32(fp.read(4))):
  171.                 left = i16(fp.read(2))
  172.                 right = i16(fp.read(2))
  173.                 width = i16(fp.read(2))
  174.                 ascent = i16(fp.read(2))
  175.                 descent = i16(fp.read(2))
  176.                 attributes = i16(fp.read(2))
  177.                 append((xsize, ysize, left, right, width, ascent, descent, attributes))
  178.  
  179.         return metrics
  180.  
  181.     def _load_bitmaps(self, metrics):
  182.  
  183.         #
  184.         # bitmap data
  185.  
  186.         bitmaps = []
  187.  
  188.         fp, format, i16, i32 = self._getformat(PCF_BITMAPS)
  189.  
  190.         nbitmaps = i32(fp.read(4))
  191.  
  192.         if nbitmaps != len(metrics):
  193.             raise IOError, "Wrong number of bitmaps"
  194.  
  195.         offsets = []
  196.         for i in range(nbitmaps):
  197.             offsets.append(i32(fp.read(4)))
  198.  
  199.         bitmapSizes = []
  200.         for i in range(4):
  201.             bitmapSizes.append(i32(fp.read(4)))
  202.  
  203.         byteorder = format & 4 # non-zero => MSB
  204.         bitorder  = format & 8 # non-zero => MSB
  205.         padindex  = format & 3
  206.  
  207.         bitmapsize = bitmapSizes[padindex]
  208.         offsets.append(bitmapsize)
  209.  
  210.         data = fp.read(bitmapsize)
  211.  
  212.         pad  = BYTES_PER_ROW[padindex]
  213.         mode = "1;R"
  214.         if bitorder:
  215.             mode = "1"
  216.  
  217.         for i in range(nbitmaps):
  218.  
  219.             x, y, l, r, w, a, d, f = metrics[i]
  220.             b, e = offsets[i], offsets[i+1]
  221.  
  222.             im = Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x))
  223.  
  224.             bitmaps.append(im)
  225.  
  226.         return bitmaps
  227.  
  228.     def _load_encoding(self):
  229.  
  230.         encoding = [None] * 256
  231.  
  232.         fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS)
  233.  
  234.         firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2))
  235.         firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2))
  236.  
  237.         default = i16(fp.read(2))
  238.  
  239.         nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1)
  240.  
  241.         for i in range(nencoding):
  242.             encodingOffset = i16(fp.read(2))
  243.             if encodingOffset != 0xFFFF:
  244.                 encoding[i+firstCol] = encodingOffset
  245.  
  246.         return encoding
  247.